<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="robots" content="noodp" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
        <title>Mybatis加载流程分析（二）- mybatis-config解析 | Janyd Blog</title>
        <meta name="Description" content="Janyd Blog"><meta property="og:title" content="Mybatis加载流程分析（二）- mybatis-config解析" />
<meta property="og:description" content="Mybatis mybati-config.xml解析 从上一篇文章我们知道XMLConfigBuilder是对mybatis-config.xml进行解析" />
<meta property="og:type" content="article" />
<meta property="og:url" content="http://example.org/mybatis%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90%E4%BA%8C-mybatis-config%E8%A7%A3%E6%9E%90/" />
<meta property="article:published_time" content="2020-04-25T17:18:07+08:00" />
<meta property="article:modified_time" content="2020-04-25T17:18:07+08:00" />
<meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="Mybatis加载流程分析（二）- mybatis-config解析"/>
<meta name="twitter:description" content="Mybatis mybati-config.xml解析 从上一篇文章我们知道XMLConfigBuilder是对mybatis-config.xml进行解析"/>
<meta name="theme-color" content="#ffffff">
<meta name="msapplication-TileColor" content="#da532c">
<link rel="canonical" href="http://example.org/mybatis%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90%E4%BA%8C-mybatis-config%E8%A7%A3%E6%9E%90/" />
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5"><link rel="prev" href="http://example.org/mybatis%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90%E4%B8%80-%E5%BC%80%E7%AF%87/" /><link rel="next" href="http://example.org/mybatis%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90%E4%B8%89-%E5%8A%A0%E8%BD%BDmybatis-config.xml%E5%86%85%E5%AE%B9/" /><link rel="stylesheet" href="/lib/fontawesome-free/all.min.css"><link rel="stylesheet" href="/lib/animate/animate.min.css"><link rel="stylesheet" href="/css/style.min.css"><script type="application/ld+json">
    {
        "@context": "http://schema.org",
        "@type": "BlogPosting",
        "headline": "Mybatis加载流程分析（二）- mybatis-config解析",
        "inLanguage": "zh-CN",
        "mainEntityOfPage": {
            "@type": "WebPage",
            "@id": "http:\/\/example.org\/mybatis%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90%E4%BA%8C-mybatis-config%E8%A7%A3%E6%9E%90\/"
        },"image": {
                "@type": "ImageObject",
                "url": "http:\/\/example.org\/cover.png",
                "width":  800 ,
                "height":  600 
            },"genre": "posts","keywords": "Mybatis, 源码","wordcount":  3528 ,
        "url": "http:\/\/example.org\/mybatis%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90%E4%BA%8C-mybatis-config%E8%A7%A3%E6%9E%90\/","datePublished": "2020-04-25","dateModified": "2020-04-25","publisher": {
                "@type": "Organization",
                "name": "Janyd",
                "logo": {
                "@type": "ImageObject",
                "url": "http:\/\/example.org\/logo.png",
                "width":  127 ,
                "height":  40 
                }
            },"author": {
                "@type": "Person",
                "name": "Janyd"
            },"description": ""
    }
    </script></head>
    <body><script>
            if (!window.localStorage || !window.localStorage.getItem('theme')) {window.isDark = 'dark' === 'dark';} else {
                window.isDark = (window.localStorage && window.localStorage.getItem('theme')) === 'dark';
            }
            window.isDark && document.body.classList.add('dark-theme');
        </script><div class="wrapper"><header class="desktop" id="header-desktop">
    <div class="header-wrapper">
        <div class="header-title">
            <a href="/">Janyd Blog</a>
        </div>
        <div class="menu"><a class="menu-item" href="/posts/" rel="noopener noreffer">文章</a><a class="menu-item" href="/tags/" rel="noopener noreffer">标签</a><a class="menu-item" href="/categories/" rel="noopener noreffer">分类</a><span class="menu-item">|</span>
            <a href="javascript:void(0);" class="menu-item theme-switch" title="切换主题">
                <i class="fas fa-adjust fa-fw"></i>
            </a></div>
    </div>
</header><header class="mobile" id="header-mobile">
    <div class="header-wrapper">
        <div class="header-container">
            <div class="header-title">
                <a href="/">Janyd Blog</a>
            </div>
            <div class="menu-toggle" id="menu-toggle-mobile">
                <span></span><span></span><span></span>
            </div>
        </div>
        <div class="menu" id="menu-mobile"><a class="menu-item" href="/posts/" title="" rel="noopener noreffer">文章</a><a class="menu-item" href="/tags/" title="" rel="noopener noreffer">标签</a><a class="menu-item" href="/categories/" title="" rel="noopener noreffer">分类</a><a href="javascript:void(0);" class="menu-item theme-switch" title="切换主题">
                <i class="fas fa-adjust fa-fw"></i>
            </a></div>
    </div>
</header>

<script>
    window.desktopHeaderMode = "fixed";
    window.mobileHeaderMode = "auto";
</script>
<main class="main">
                <div class="container"><article class="page single"><h1 class="single-title animated flipInX">Mybatis加载流程分析（二）- mybatis-config解析</h1><div class="post-meta">
            <div class="post-meta-line"><span class="post-author"><a href="https://github.com/Janyd" title="Author" target="_blank" rel="noopener noreffer author" class="author"><i class="fas fa-user-circle fa-fw"></i>Janyd</a>
</span>&nbsp;
                    <span class="post-category">收录于<a href="/categories/mybatis%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90">
                                <i class="far fa-folder fa-fw"></i>Mybatis源码解析
                            </a></span></div>
            <div class="post-meta-line"><i class="far fa-calendar-alt fa-fw"></i><time datetime=2020-04-25>2020-04-25</time>&nbsp;
                <i class="fas fa-pencil-alt fa-fw"></i>约 3528 字&nbsp;
                <i class="far fa-clock fa-fw"></i>预计阅读 8 分钟&nbsp;</div>
        </div><div class="toc" id="toc-auto">
                <h2 class="toc-title">目录</h2>
                <div class="toc-content" id="toc-content-auto"></div>
            </div>
            <div class="toc" id="toc-static">
                <details>
                    <summary>
                        <div class="toc-title">
                            <span>目录</span>
                            <span><i class="details icon fas fa-angle-down"></i></span>
                        </div>
                    </summary>
                    <div class="toc-content" id="toc-content-static"><nav id="TableOfContents">
  <ul>
    <li><a href="#mybatis-mybati-configxml解析">Mybatis mybati-config.xml解析</a>
      <ul>
        <li><a href="#xmlconfigbuilder解析器"><code>XMLConfigBuilder</code>解析器</a></li>
        <li><a href="#xpathparser解析器"><code>XPathParser</code>解析器</a>
          <ul>
            <li><a href="#xpathparser构造方法"><code>XPathParser</code>构造方法</a></li>
          </ul>
        </li>
        <li><a href="#xnode解析动态配置"><code>XNode</code>解析动态配置</a></li>
      </ul>
    </li>
    <li><a href="#总结">总结</a></li>
    <li><a href="#参考链接">参考链接</a></li>
  </ul>
</nav></div>
                </details>
            </div><div class="content" id="content"><h2 id="mybatis-mybati-configxml解析">Mybatis mybati-config.xml解析</h2>
<p>从上一篇文章我们知道XMLConfigBuilder是对<code>mybatis-config.xml</code>进行解析，<code>XMLConfigBuilder</code>还有个抽象基类<code>BaseBuilder</code>，其中没有抽象方法仅仅抽取了公共方法。
<div class="mermaid" id="id-d23ec7"></div>
从以上类图看出<code>BaseBuilder</code>还有其他子类，有些从名称可以看出其作用，后续我们也会讲解。</p>
<p>我们先来看<code>XMLConfigBuilder</code>类的结构：</p>
<h3 id="xmlconfigbuilder解析器"><code>XMLConfigBuilder</code>解析器</h3>
<p><code>XMLConfigBuilder</code>有六个构造方法，但最终调用的也只有一个构造方法</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-Java" data-lang="Java"><span class="c1">//是否已解析
</span><span class="c1"></span><span class="kd">private</span> <span class="kt">boolean</span> <span class="n">parsed</span><span class="o">;</span>
<span class="c1">//Xpath解析器
</span><span class="c1"></span><span class="kd">private</span> <span class="kd">final</span> <span class="n">XPathParser</span> <span class="n">parser</span><span class="o">;</span>
<span class="c1">//环境标识
</span><span class="c1"></span><span class="kd">private</span> <span class="n">String</span> <span class="n">environment</span><span class="o">;</span>
<span class="c1">//反射器工厂
</span><span class="c1"></span><span class="kd">private</span> <span class="kd">final</span> <span class="n">ReflectorFactory</span> <span class="n">localReflectorFactory</span> <span class="o">=</span> <span class="k">new</span> <span class="n">DefaultReflectorFactory</span><span class="o">();</span>

<span class="kd">public</span> <span class="nf">XMLConfigBuilder</span><span class="o">(</span><span class="n">Reader</span> <span class="n">reader</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">(</span><span class="n">reader</span><span class="o">,</span> <span class="kc">null</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="nf">XMLConfigBuilder</span><span class="o">(</span><span class="n">Reader</span> <span class="n">reader</span><span class="o">,</span> <span class="n">String</span> <span class="n">environment</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">(</span><span class="n">reader</span><span class="o">,</span> <span class="n">environment</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="nf">XMLConfigBuilder</span><span class="o">(</span><span class="n">Reader</span> <span class="n">reader</span><span class="o">,</span> <span class="n">String</span> <span class="n">environment</span><span class="o">,</span> <span class="n">Properties</span> <span class="n">props</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">(</span><span class="k">new</span> <span class="n">XPathParser</span><span class="o">(</span><span class="n">reader</span><span class="o">,</span> <span class="kc">true</span><span class="o">,</span> <span class="n">props</span><span class="o">,</span> <span class="k">new</span> <span class="n">XMLMapperEntityResolver</span><span class="o">()),</span> <span class="n">environment</span><span class="o">,</span> <span class="n">props</span><span class="o">);</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="nf">XMLConfigBuilder</span><span class="o">(</span><span class="n">InputStream</span> <span class="n">inputStream</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">(</span><span class="n">inputStream</span><span class="o">,</span> <span class="kc">null</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="nf">XMLConfigBuilder</span><span class="o">(</span><span class="n">InputStream</span> <span class="n">inputStream</span><span class="o">,</span> <span class="n">String</span> <span class="n">environment</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">(</span><span class="n">inputStream</span><span class="o">,</span> <span class="n">environment</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="nf">XMLConfigBuilder</span><span class="o">(</span><span class="n">InputStream</span> <span class="n">inputStream</span><span class="o">,</span> <span class="n">String</span> <span class="n">environment</span><span class="o">,</span> <span class="n">Properties</span> <span class="n">props</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">(</span><span class="k">new</span> <span class="n">XPathParser</span><span class="o">(</span><span class="n">inputStream</span><span class="o">,</span> <span class="kc">true</span><span class="o">,</span> <span class="n">props</span><span class="o">,</span> <span class="k">new</span> <span class="n">XMLMapperEntityResolver</span><span class="o">()),</span> <span class="n">environment</span><span class="o">,</span> <span class="n">props</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="nf">XMLConfigBuilder</span><span class="o">(</span><span class="n">XPathParser</span> <span class="n">parser</span><span class="o">,</span> <span class="n">String</span> <span class="n">environment</span><span class="o">,</span> <span class="n">Properties</span> <span class="n">props</span><span class="o">)</span> <span class="o">{</span>
    <span class="c1">//1.调用父类构造方法
</span><span class="c1"></span>    <span class="kd">super</span><span class="o">(</span><span class="k">new</span> <span class="n">Configuration</span><span class="o">());</span>
    <span class="n">ErrorContext</span><span class="o">.</span><span class="na">instance</span><span class="o">().</span><span class="na">resource</span><span class="o">(</span><span class="s">&#34;SQL Mapper Configuration&#34;</span><span class="o">);</span>
    <span class="k">this</span><span class="o">.</span><span class="na">configuration</span><span class="o">.</span><span class="na">setVariables</span><span class="o">(</span><span class="n">props</span><span class="o">);</span>
    <span class="k">this</span><span class="o">.</span><span class="na">parsed</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>
    <span class="k">this</span><span class="o">.</span><span class="na">environment</span> <span class="o">=</span> <span class="n">environment</span><span class="o">;</span>
    <span class="k">this</span><span class="o">.</span><span class="na">parser</span> <span class="o">=</span> <span class="n">parser</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></td></tr></table>
</div>
</div><p>前面五个构造方法都是需要传入配置文件输入流，最终由<code>XPathParser</code>来读取，最后一个构造方法：</p>
<ol>
<li>调用父类构造方法，创建<code>Configuration</code></li>
<li>设置错误上下文的当前位置信息</li>
<li>是否已解析设置为false</li>
<li>环境标识</li>
<li>解析器</li>
</ol>
<p>我们再看看父类的构造函数：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-Java" data-lang="Java"><span class="kd">protected</span> <span class="kd">final</span> <span class="n">Configuration</span> <span class="n">configuration</span><span class="o">;</span>
<span class="kd">protected</span> <span class="kd">final</span> <span class="n">TypeAliasRegistry</span> <span class="n">typeAliasRegistry</span><span class="o">;</span>
<span class="kd">protected</span> <span class="kd">final</span> <span class="n">TypeHandlerRegistry</span> <span class="n">typeHandlerRegistry</span><span class="o">;</span>

<span class="kd">public</span> <span class="nf">BaseBuilder</span><span class="o">(</span><span class="n">Configuration</span> <span class="n">configuration</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">.</span><span class="na">configuration</span> <span class="o">=</span> <span class="n">configuration</span><span class="o">;</span>
    <span class="k">this</span><span class="o">.</span><span class="na">typeAliasRegistry</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">configuration</span><span class="o">.</span><span class="na">getTypeAliasRegistry</span><span class="o">();</span>
    <span class="k">this</span><span class="o">.</span><span class="na">typeHandlerRegistry</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">configuration</span><span class="o">.</span><span class="na">getTypeHandlerRegistry</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></td></tr></table>
</div>
</div><p>就是从<code>Configuration</code>取出别名注册类型与类型处理注册类方便在Builder类们所使用（这两个类在<code>Configuration</code>创建过程就初始化了，详情可看<code>Configuration</code>）</p>
<h3 id="xpathparser解析器"><code>XPathParser</code>解析器</h3>
<p>该解析器位于<code>org.apache.ibatis.parsing</code>包中，里面包含了：<br>
<img
        class="lazyload"
        src="/svg/loading.small.min.svg"
        data-sizes="auto"
        data-srcset="https://cdn.jsdelivr.net/gh/Janyd/blog-images/imgs/20200425180512.png,  1.5x,  2x"
        data-src=""
        alt=""
        title="" /><br>
<code>XPathParser</code>解析器是基于java提供的API来进行封装提供给Mybatis进行对XML的解析</p>
<h4 id="xpathparser构造方法"><code>XPathParser</code>构造方法</h4>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span><span class="lnt">59
</span><span class="lnt">60
</span><span class="lnt">61
</span><span class="lnt">62
</span><span class="lnt">63
</span><span class="lnt">64
</span><span class="lnt">65
</span><span class="lnt">66
</span><span class="lnt">67
</span><span class="lnt">68
</span><span class="lnt">69
</span><span class="lnt">70
</span><span class="lnt">71
</span><span class="lnt">72
</span><span class="lnt">73
</span><span class="lnt">74
</span><span class="lnt">75
</span><span class="lnt">76
</span><span class="lnt">77
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-Java" data-lang="Java"><span class="cm">/**
</span><span class="cm"> * XML的Document内容
</span><span class="cm"> */</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="n">Document</span> <span class="n">document</span><span class="o">;</span>


<span class="kd">private</span> <span class="kt">boolean</span> <span class="n">validation</span><span class="o">;</span>

<span class="cm">/**
</span><span class="cm"> * XML 实体解析器，加载DTD或XSD文件
</span><span class="cm"> */</span>
<span class="kd">private</span> <span class="n">EntityResolver</span> <span class="n">entityResolver</span><span class="o">;</span>

<span class="cm">/**
</span><span class="cm"> * 加载变量，例如xml里有${}占位符时，需要通过额外的文件去加载变量值，动态替换属性值
</span><span class="cm"> */</span>
<span class="kd">private</span> <span class="n">Properties</span> <span class="n">variables</span><span class="o">;</span>

<span class="cm">/**
</span><span class="cm"> * Java Xpath 用于查询XML节点信息等等
</span><span class="cm"> */</span>
<span class="kd">private</span> <span class="n">XPath</span> <span class="n">xpath</span><span class="o">;</span>

<span class="cm">/**
</span><span class="cm"> * 公共构造函数
</span><span class="cm"> *
</span><span class="cm"> * @param validation     是否校验XML
</span><span class="cm"> * @param variables      动态配置属性值
</span><span class="cm"> * @param entityResolver XML实体解析器
</span><span class="cm">*/</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">commonConstructor</span><span class="o">(</span><span class="kt">boolean</span> <span class="n">validation</span><span class="o">,</span> <span class="n">Properties</span> <span class="n">variables</span><span class="o">,</span> <span class="n">EntityResolver</span> <span class="n">entityResolver</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">.</span><span class="na">validation</span> <span class="o">=</span> <span class="n">validation</span><span class="o">;</span>
    <span class="k">this</span><span class="o">.</span><span class="na">entityResolver</span> <span class="o">=</span> <span class="n">entityResolver</span><span class="o">;</span>
    <span class="k">this</span><span class="o">.</span><span class="na">variables</span> <span class="o">=</span> <span class="n">variables</span><span class="o">;</span>
    <span class="n">XPathFactory</span> <span class="n">factory</span> <span class="o">=</span> <span class="n">XPathFactory</span><span class="o">.</span><span class="na">newInstance</span><span class="o">();</span>
    <span class="k">this</span><span class="o">.</span><span class="na">xpath</span> <span class="o">=</span> <span class="n">factory</span><span class="o">.</span><span class="na">newXPath</span><span class="o">();</span>
<span class="o">}</span>

<span class="kd">private</span> <span class="n">Document</span> <span class="nf">createDocument</span><span class="o">(</span><span class="n">InputSource</span> <span class="n">inputSource</span><span class="o">)</span> <span class="o">{</span>
    <span class="c1">// important: this must only be called AFTER common constructor
</span><span class="c1"></span>    <span class="k">try</span> <span class="o">{</span>
        <span class="n">DocumentBuilderFactory</span> <span class="n">factory</span> <span class="o">=</span> <span class="n">DocumentBuilderFactory</span><span class="o">.</span><span class="na">newInstance</span><span class="o">();</span>
        <span class="n">factory</span><span class="o">.</span><span class="na">setFeature</span><span class="o">(</span><span class="n">XMLConstants</span><span class="o">.</span><span class="na">FEATURE_SECURE_PROCESSING</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span>
        <span class="c1">//是否校验XML
</span><span class="c1"></span>        <span class="n">factory</span><span class="o">.</span><span class="na">setValidating</span><span class="o">(</span><span class="n">validation</span><span class="o">);</span>

        <span class="n">factory</span><span class="o">.</span><span class="na">setNamespaceAware</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
        <span class="n">factory</span><span class="o">.</span><span class="na">setIgnoringComments</span><span class="o">(</span><span class="kc">true</span><span class="o">);</span>
        <span class="n">factory</span><span class="o">.</span><span class="na">setIgnoringElementContentWhitespace</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
        <span class="n">factory</span><span class="o">.</span><span class="na">setCoalescing</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
        <span class="n">factory</span><span class="o">.</span><span class="na">setExpandEntityReferences</span><span class="o">(</span><span class="kc">true</span><span class="o">);</span>

        <span class="n">DocumentBuilder</span> <span class="n">builder</span> <span class="o">=</span> <span class="n">factory</span><span class="o">.</span><span class="na">newDocumentBuilder</span><span class="o">();</span>
        <span class="n">builder</span><span class="o">.</span><span class="na">setEntityResolver</span><span class="o">(</span><span class="n">entityResolver</span><span class="o">);</span>

        <span class="c1">//错误处理器
</span><span class="c1"></span>        <span class="n">builder</span><span class="o">.</span><span class="na">setErrorHandler</span><span class="o">(</span><span class="k">new</span> <span class="n">ErrorHandler</span><span class="o">()</span> <span class="o">{</span>
            <span class="nd">@Override</span>
            <span class="kd">public</span> <span class="kt">void</span> <span class="nf">error</span><span class="o">(</span><span class="n">SAXParseException</span> <span class="n">exception</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">SAXException</span> <span class="o">{</span>
                <span class="k">throw</span> <span class="n">exception</span><span class="o">;</span>
            <span class="o">}</span>

            <span class="nd">@Override</span>
            <span class="kd">public</span> <span class="kt">void</span> <span class="nf">fatalError</span><span class="o">(</span><span class="n">SAXParseException</span> <span class="n">exception</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">SAXException</span> <span class="o">{</span>
                <span class="k">throw</span> <span class="n">exception</span><span class="o">;</span>
            <span class="o">}</span>

            <span class="nd">@Override</span>
            <span class="kd">public</span> <span class="kt">void</span> <span class="nf">warning</span><span class="o">(</span><span class="n">SAXParseException</span> <span class="n">exception</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">SAXException</span> <span class="o">{</span>
                <span class="c1">// NOP
</span><span class="c1"></span>            <span class="o">}</span>
        <span class="o">});</span>
        <span class="k">return</span> <span class="n">builder</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="n">inputSource</span><span class="o">);</span>
    <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="n">BuilderException</span><span class="o">(</span><span class="s">&#34;Error creating document instance.  Cause: &#34;</span> <span class="o">+</span> <span class="n">e</span><span class="o">,</span> <span class="n">e</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></td></tr></table>
</div>
</div><p><code>XPathParser</code>构造方法有很多，但基本都比较类似，都会调用<code>commonConstructor</code>与<code>createDocument</code>两个方法，属性的作用如下：</p>
<ul>
<li><code>document</code>也就是XML的内容</li>
<li><code>validation</code>是否验证XML的正确性</li>
<li><code>entityResolver</code>XML的实体解析器，用于加载XML的DTD或XSD文件</li>
<li><code>variables</code>动态变量</li>
<li><code>xpath</code> 用于查询XML节点信息</li>
</ul>
<p><code>createDocument</code>方法时为了创建<code>Document</code>，<code>Document</code>是<code>org.w3c.dom</code>包是java提供的。</p>
<p>其他的方法都是<code>eval*</code>的方法族，用于根据xpath表达式获取节点上的数据，我们重点来看<code>evaluate</code>方法：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-Java" data-lang="Java"><span class="cm">/**
</span><span class="cm"> * 解析xpath表达式
</span><span class="cm"> *
</span><span class="cm"> * @param expression 表达式
</span><span class="cm"> * @param root       节点对象，从哪个节点开始
</span><span class="cm"> * @param returnType 返回的类型
</span><span class="cm"> * @return 数据
</span><span class="cm"> */</span>
<span class="kd">private</span> <span class="n">Object</span> <span class="nf">evaluate</span><span class="o">(</span><span class="n">String</span> <span class="n">expression</span><span class="o">,</span> <span class="n">Object</span> <span class="n">root</span><span class="o">,</span> <span class="n">QName</span> <span class="n">returnType</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">try</span> <span class="o">{</span>
        <span class="c1">//利用xpath解析表达式
</span><span class="c1"></span>        <span class="k">return</span> <span class="n">xpath</span><span class="o">.</span><span class="na">evaluate</span><span class="o">(</span><span class="n">expression</span><span class="o">,</span> <span class="n">root</span><span class="o">,</span> <span class="n">returnType</span><span class="o">);</span>
    <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="n">BuilderException</span><span class="o">(</span><span class="s">&#34;Error evaluating XPath.  Cause: &#34;</span> <span class="o">+</span> <span class="n">e</span><span class="o">,</span> <span class="n">e</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></td></tr></table>
</div>
</div><p>其他方法都是基于该方法来进行解析完成，但有部分是解析出节点对象<code>XNode</code></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-Java" data-lang="Java"><span class="kd">public</span> <span class="n">XNode</span> <span class="nf">evalNode</span><span class="o">(</span><span class="n">Object</span> <span class="n">root</span><span class="o">,</span> <span class="n">String</span> <span class="n">expression</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">Node</span> <span class="n">node</span> <span class="o">=</span> <span class="o">(</span><span class="n">Node</span><span class="o">)</span> <span class="n">evaluate</span><span class="o">(</span><span class="n">expression</span><span class="o">,</span> <span class="n">root</span><span class="o">,</span> <span class="n">XPathConstants</span><span class="o">.</span><span class="na">NODE</span><span class="o">);</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">node</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="c1">//封装成 @see org.apache.ibatis.parsing.XNode 主要是为了能够解析${xx} 动态值
</span><span class="c1"></span>    <span class="k">return</span> <span class="k">new</span> <span class="n">XNode</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="n">node</span><span class="o">,</span> <span class="n">variables</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">XNode</span><span class="o">&gt;</span> <span class="nf">evalNodes</span><span class="o">(</span><span class="n">Object</span> <span class="n">root</span><span class="o">,</span> <span class="n">String</span> <span class="n">expression</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">List</span><span class="o">&lt;</span><span class="n">XNode</span><span class="o">&gt;</span> <span class="n">xnodes</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ArrayList</span><span class="o">&lt;&gt;();</span>
        <span class="n">NodeList</span> <span class="n">nodes</span> <span class="o">=</span> <span class="o">(</span><span class="n">NodeList</span><span class="o">)</span> <span class="n">evaluate</span><span class="o">(</span><span class="n">expression</span><span class="o">,</span> <span class="n">root</span><span class="o">,</span> <span class="n">XPathConstants</span><span class="o">.</span><span class="na">NODESET</span><span class="o">);</span>
        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">nodes</span><span class="o">.</span><span class="na">getLength</span><span class="o">();</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
            <span class="n">xnodes</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="k">new</span> <span class="n">XNode</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="n">nodes</span><span class="o">.</span><span class="na">item</span><span class="o">(</span><span class="n">i</span><span class="o">),</span> <span class="n">variables</span><span class="o">));</span>
        <span class="o">}</span>
        <span class="k">return</span> <span class="n">xnodes</span><span class="o">;</span>
    <span class="o">}</span>
</code></pre></td></tr></table>
</div>
</div><p><code>XNode</code>是mybatis对<code>org.w3c.dom.Node</code>的一个封装，增强节点对象的解析与动态变量的替换。
从上篇文章我们知道<code>XMLConfigBuilder#parseConfiguration</code>方法的参数就是<code>XNode</code>，这个这节点就是<code>mybatis-config.xml</code>的根节点<code>configuration</code>。</p>
<h3 id="xnode解析动态配置"><code>XNode</code>解析动态配置</h3>
<p>在<code>mybatis-config.xml</code>可以使用${}占位符来代表该变量是动态的，这个变量是从properties读取或在配置文件中节点<code>&lt;properties /&gt;</code>读取的动态变量，而这里如何做到的呢？该相关的逻辑都在<code>XNode</code>与<code>PropertyParser</code>中，<code>XNode</code>获取标签属性时，会经过动态变量进行替换再返回，例如：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-Java" data-lang="Java"><span class="kd">private</span> <span class="n">Properties</span> <span class="nf">parseAttributes</span><span class="o">(</span><span class="n">Node</span> <span class="n">n</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">Properties</span> <span class="n">attributes</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Properties</span><span class="o">();</span>
    <span class="n">NamedNodeMap</span> <span class="n">attributeNodes</span> <span class="o">=</span> <span class="n">n</span><span class="o">.</span><span class="na">getAttributes</span><span class="o">();</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">attributeNodes</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">attributeNodes</span><span class="o">.</span><span class="na">getLength</span><span class="o">();</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
            <span class="n">Node</span> <span class="n">attribute</span> <span class="o">=</span> <span class="n">attributeNodes</span><span class="o">.</span><span class="na">item</span><span class="o">(</span><span class="n">i</span><span class="o">);</span>
            <span class="c1">//此处调用PropertyParser#parse
</span><span class="c1"></span>            <span class="n">String</span> <span class="n">value</span> <span class="o">=</span> <span class="n">PropertyParser</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="n">attribute</span><span class="o">.</span><span class="na">getNodeValue</span><span class="o">(),</span> <span class="n">variables</span><span class="o">);</span>
            <span class="n">attributes</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">attribute</span><span class="o">.</span><span class="na">getNodeName</span><span class="o">(),</span> <span class="n">value</span><span class="o">);</span>
        <span class="o">}</span>
    <span class="o">}</span>
    <span class="k">return</span> <span class="n">attributes</span><span class="o">;</span>
<span class="o">}</span>

<span class="c1">//PropertyParser.java类
</span><span class="c1"></span><span class="kd">public</span> <span class="kd">static</span> <span class="n">String</span> <span class="nf">parse</span><span class="o">(</span><span class="n">String</span> <span class="n">string</span><span class="o">,</span> <span class="n">Properties</span> <span class="n">variables</span><span class="o">)</span> <span class="o">{</span>
    <span class="c1">//变量处理器
</span><span class="c1"></span>    <span class="n">VariableTokenHandler</span> <span class="n">handler</span> <span class="o">=</span> <span class="k">new</span> <span class="n">VariableTokenHandler</span><span class="o">(</span><span class="n">variables</span><span class="o">);</span>

    <span class="c1">//通用解析器
</span><span class="c1"></span>    <span class="n">GenericTokenParser</span> <span class="n">parser</span> <span class="o">=</span> <span class="k">new</span> <span class="n">GenericTokenParser</span><span class="o">(</span><span class="s">&#34;${&#34;</span><span class="o">,</span> <span class="s">&#34;}&#34;</span><span class="o">,</span> <span class="n">handler</span><span class="o">);</span>
    <span class="c1">//解析执行
</span><span class="c1"></span>    <span class="k">return</span> <span class="n">parser</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="n">string</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></td></tr></table>
</div>
</div><p>从代码可以看出使用了<code>VariableTokenHandler</code>与<code>GenericTokenParser</code>来进行解析，进一步查看<code>VariableTokenHandler</code>是<code>PropertyParser</code>内部类，并实现了接口<code>TokenHandler</code></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-Java" data-lang="Java"><span class="kd">private</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">VariableTokenHandler</span> <span class="kd">implements</span> <span class="n">TokenHandler</span> <span class="o">{</span>

    <span class="c1">//动态变量
</span><span class="c1"></span>    <span class="kd">private</span> <span class="kd">final</span> <span class="n">Properties</span> <span class="n">variables</span><span class="o">;</span>
    <span class="c1">//是否开启默认值
</span><span class="c1"></span>    <span class="kd">private</span> <span class="kd">final</span> <span class="kt">boolean</span> <span class="n">enableDefaultValue</span><span class="o">;</span>
    <span class="c1">//默认值的分隔符
</span><span class="c1"></span>    <span class="kd">private</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">defaultValueSeparator</span><span class="o">;</span>

    <span class="kd">private</span> <span class="nf">VariableTokenHandler</span><span class="o">(</span><span class="n">Properties</span> <span class="n">variables</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">variables</span> <span class="o">=</span> <span class="n">variables</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">enableDefaultValue</span> <span class="o">=</span> <span class="n">Boolean</span><span class="o">.</span><span class="na">parseBoolean</span><span class="o">(</span><span class="n">getPropertyValue</span><span class="o">(</span><span class="n">KEY_ENABLE_DEFAULT_VALUE</span><span class="o">,</span> <span class="n">ENABLE_DEFAULT_VALUE</span><span class="o">));</span>
        <span class="k">this</span><span class="o">.</span><span class="na">defaultValueSeparator</span> <span class="o">=</span> <span class="n">getPropertyValue</span><span class="o">(</span><span class="n">KEY_DEFAULT_VALUE_SEPARATOR</span><span class="o">,</span> <span class="n">DEFAULT_VALUE_SEPARATOR</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="kd">private</span> <span class="n">String</span> <span class="nf">getPropertyValue</span><span class="o">(</span><span class="n">String</span> <span class="n">key</span><span class="o">,</span> <span class="n">String</span> <span class="n">defaultValue</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">return</span> <span class="o">(</span><span class="n">variables</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="n">defaultValue</span> <span class="o">:</span> <span class="n">variables</span><span class="o">.</span><span class="na">getProperty</span><span class="o">(</span><span class="n">key</span><span class="o">,</span> <span class="n">defaultValue</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="n">String</span> <span class="nf">handleToken</span><span class="o">(</span><span class="n">String</span> <span class="n">content</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">variables</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">String</span> <span class="n">key</span> <span class="o">=</span> <span class="n">content</span><span class="o">;</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">enableDefaultValue</span><span class="o">)</span> <span class="o">{</span>
                <span class="c1">//如果开启默认值，解析content里有没有 冒号(:)，冒号前是动态变量的key，冒号后面是默认值，如果key不存在variables里，就会取默认值
</span><span class="c1"></span>                <span class="kd">final</span> <span class="kt">int</span> <span class="n">separatorIndex</span> <span class="o">=</span> <span class="n">content</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="n">defaultValueSeparator</span><span class="o">);</span>
                <span class="n">String</span> <span class="n">defaultValue</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
                <span class="k">if</span> <span class="o">(</span><span class="n">separatorIndex</span> <span class="o">&gt;=</span> <span class="n">0</span><span class="o">)</span> <span class="o">{</span>
                    <span class="n">key</span> <span class="o">=</span> <span class="n">content</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="n">0</span><span class="o">,</span> <span class="n">separatorIndex</span><span class="o">);</span>
                    <span class="n">defaultValue</span> <span class="o">=</span> <span class="n">content</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="n">separatorIndex</span> <span class="o">+</span> <span class="n">defaultValueSeparator</span><span class="o">.</span><span class="na">length</span><span class="o">());</span>
                <span class="o">}</span>
                <span class="k">if</span> <span class="o">(</span><span class="n">defaultValue</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
                    <span class="k">return</span> <span class="n">variables</span><span class="o">.</span><span class="na">getProperty</span><span class="o">(</span><span class="n">key</span><span class="o">,</span> <span class="n">defaultValue</span><span class="o">);</span>
                <span class="o">}</span>
            <span class="o">}</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">variables</span><span class="o">.</span><span class="na">containsKey</span><span class="o">(</span><span class="n">key</span><span class="o">))</span> <span class="o">{</span>
                <span class="k">return</span> <span class="n">variables</span><span class="o">.</span><span class="na">getProperty</span><span class="o">(</span><span class="n">key</span><span class="o">);</span>
            <span class="o">}</span>
        <span class="o">}</span>
        <span class="k">return</span> <span class="s">&#34;${&#34;</span> <span class="o">+</span> <span class="n">content</span> <span class="o">+</span> <span class="s">&#34;}&#34;</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></td></tr></table>
</div>
</div><p>从代码可看出其实就是处理content，并返回<code>variables</code>中的变量值，只是这里还含有默认值的处理，现在大多数都会采取这样的默认规则${key:defaultValue}</p>
<p><code>GenericTokenParser</code>是通用解析器，怎么通用呢？源码如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span><span class="lnt">59
</span><span class="lnt">60
</span><span class="lnt">61
</span><span class="lnt">62
</span><span class="lnt">63
</span><span class="lnt">64
</span><span class="lnt">65
</span><span class="lnt">66
</span><span class="lnt">67
</span><span class="lnt">68
</span><span class="lnt">69
</span><span class="lnt">70
</span><span class="lnt">71
</span><span class="lnt">72
</span><span class="lnt">73
</span><span class="lnt">74
</span><span class="lnt">75
</span><span class="lnt">76
</span><span class="lnt">77
</span><span class="lnt">78
</span><span class="lnt">79
</span><span class="lnt">80
</span><span class="lnt">81
</span><span class="lnt">82
</span><span class="lnt">83
</span><span class="lnt">84
</span><span class="lnt">85
</span><span class="lnt">86
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-Java" data-lang="Java"><span class="cm">/**
</span><span class="cm"> * 通用token解析器
</span><span class="cm"> *
</span><span class="cm"> * @author Clinton Begin
</span><span class="cm"> */</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">GenericTokenParser</span> <span class="o">{</span>

    <span class="cm">/**
</span><span class="cm">     * 开始字符串
</span><span class="cm">     */</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">openToken</span><span class="o">;</span>

    <span class="cm">/**
</span><span class="cm">     * 结束字符串
</span><span class="cm">     */</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">closeToken</span><span class="o">;</span>

    <span class="cm">/**
</span><span class="cm">     * token处理器
</span><span class="cm">     */</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="n">TokenHandler</span> <span class="n">handler</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">GenericTokenParser</span><span class="o">(</span><span class="n">String</span> <span class="n">openToken</span><span class="o">,</span> <span class="n">String</span> <span class="n">closeToken</span><span class="o">,</span> <span class="n">TokenHandler</span> <span class="n">handler</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">openToken</span> <span class="o">=</span> <span class="n">openToken</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">closeToken</span> <span class="o">=</span> <span class="n">closeToken</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">handler</span> <span class="o">=</span> <span class="n">handler</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="n">String</span> <span class="nf">parse</span><span class="o">(</span><span class="n">String</span> <span class="n">text</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">text</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="n">text</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">())</span> <span class="o">{</span>
            <span class="k">return</span> <span class="s">&#34;&#34;</span><span class="o">;</span>
        <span class="o">}</span>
        <span class="c1">// search open token
</span><span class="c1"></span>        <span class="kt">int</span> <span class="n">start</span> <span class="o">=</span> <span class="n">text</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="n">openToken</span><span class="o">);</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">start</span> <span class="o">==</span> <span class="o">-</span><span class="n">1</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">return</span> <span class="n">text</span><span class="o">;</span>
        <span class="o">}</span>
        <span class="kt">char</span><span class="o">[]</span> <span class="n">src</span> <span class="o">=</span> <span class="n">text</span><span class="o">.</span><span class="na">toCharArray</span><span class="o">();</span>
        <span class="kt">int</span> <span class="n">offset</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span>
        <span class="kd">final</span> <span class="n">StringBuilder</span> <span class="n">builder</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringBuilder</span><span class="o">();</span>
        <span class="n">StringBuilder</span> <span class="n">expression</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
        <span class="k">while</span> <span class="o">(</span><span class="n">start</span> <span class="o">&gt;</span> <span class="o">-</span><span class="n">1</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">start</span> <span class="o">&gt;</span> <span class="n">0</span> <span class="o">&amp;&amp;</span> <span class="n">src</span><span class="o">[</span><span class="n">start</span> <span class="o">-</span> <span class="n">1</span><span class="o">]</span> <span class="o">==</span> <span class="sc">&#39;\\&#39;</span><span class="o">)</span> <span class="o">{</span>
                <span class="c1">// this open token is escaped. remove the backslash and continue.
</span><span class="c1"></span>                <span class="c1">//由于openToken前是转义字符\,需要忽略
</span><span class="c1"></span>                <span class="n">builder</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">src</span><span class="o">,</span> <span class="n">offset</span><span class="o">,</span> <span class="n">start</span> <span class="o">-</span> <span class="n">offset</span> <span class="o">-</span> <span class="n">1</span><span class="o">).</span><span class="na">append</span><span class="o">(</span><span class="n">openToken</span><span class="o">);</span>
                <span class="c1">//重新定位偏移位
</span><span class="c1"></span>                <span class="n">offset</span> <span class="o">=</span> <span class="n">start</span> <span class="o">+</span> <span class="n">openToken</span><span class="o">.</span><span class="na">length</span><span class="o">();</span>
            <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
                <span class="c1">// found open token. let&#39;s search close token.
</span><span class="c1"></span>                <span class="k">if</span> <span class="o">(</span><span class="n">expression</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
                    <span class="n">expression</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringBuilder</span><span class="o">();</span>
                <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
                    <span class="n">expression</span><span class="o">.</span><span class="na">setLength</span><span class="o">(</span><span class="n">0</span><span class="o">);</span>
                <span class="o">}</span>
                <span class="n">builder</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">src</span><span class="o">,</span> <span class="n">offset</span><span class="o">,</span> <span class="n">start</span> <span class="o">-</span> <span class="n">offset</span><span class="o">);</span>
                <span class="n">offset</span> <span class="o">=</span> <span class="n">start</span> <span class="o">+</span> <span class="n">openToken</span><span class="o">.</span><span class="na">length</span><span class="o">();</span>
                <span class="kt">int</span> <span class="n">end</span> <span class="o">=</span> <span class="n">text</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="n">closeToken</span><span class="o">,</span> <span class="n">offset</span><span class="o">);</span>
                <span class="k">while</span> <span class="o">(</span><span class="n">end</span> <span class="o">&gt;</span> <span class="o">-</span><span class="n">1</span><span class="o">)</span> <span class="o">{</span>
                    <span class="k">if</span> <span class="o">(</span><span class="n">end</span> <span class="o">&gt;</span> <span class="n">offset</span> <span class="o">&amp;&amp;</span> <span class="n">src</span><span class="o">[</span><span class="n">end</span> <span class="o">-</span> <span class="n">1</span><span class="o">]</span> <span class="o">==</span> <span class="sc">&#39;\\&#39;</span><span class="o">)</span> <span class="o">{</span>
                        <span class="c1">// this close token is escaped. remove the backslash and continue.
</span><span class="c1"></span>                        <span class="n">expression</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">src</span><span class="o">,</span> <span class="n">offset</span><span class="o">,</span> <span class="n">end</span> <span class="o">-</span> <span class="n">offset</span> <span class="o">-</span> <span class="n">1</span><span class="o">).</span><span class="na">append</span><span class="o">(</span><span class="n">closeToken</span><span class="o">);</span>
                        <span class="n">offset</span> <span class="o">=</span> <span class="n">end</span> <span class="o">+</span> <span class="n">closeToken</span><span class="o">.</span><span class="na">length</span><span class="o">();</span>
                        <span class="n">end</span> <span class="o">=</span> <span class="n">text</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="n">closeToken</span><span class="o">,</span> <span class="n">offset</span><span class="o">);</span>
                    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
                        <span class="n">expression</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">src</span><span class="o">,</span> <span class="n">offset</span><span class="o">,</span> <span class="n">end</span> <span class="o">-</span> <span class="n">offset</span><span class="o">);</span>
                        <span class="k">break</span><span class="o">;</span>
                    <span class="o">}</span>
                <span class="o">}</span>
                <span class="k">if</span> <span class="o">(</span><span class="n">end</span> <span class="o">==</span> <span class="o">-</span><span class="n">1</span><span class="o">)</span> <span class="o">{</span>
                    <span class="c1">// close token was not found.
</span><span class="c1"></span>                    <span class="n">builder</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">src</span><span class="o">,</span> <span class="n">start</span><span class="o">,</span> <span class="n">src</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="n">start</span><span class="o">);</span>
                    <span class="n">offset</span> <span class="o">=</span> <span class="n">src</span><span class="o">.</span><span class="na">length</span><span class="o">;</span>
                <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
                    <span class="n">builder</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">handler</span><span class="o">.</span><span class="na">handleToken</span><span class="o">(</span><span class="n">expression</span><span class="o">.</span><span class="na">toString</span><span class="o">()));</span>
                    <span class="n">offset</span> <span class="o">=</span> <span class="n">end</span> <span class="o">+</span> <span class="n">closeToken</span><span class="o">.</span><span class="na">length</span><span class="o">();</span>
                <span class="o">}</span>
            <span class="o">}</span>
            <span class="n">start</span> <span class="o">=</span> <span class="n">text</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="n">openToken</span><span class="o">,</span> <span class="n">offset</span><span class="o">);</span>
        <span class="o">}</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">offset</span> <span class="o">&lt;</span> <span class="n">src</span><span class="o">.</span><span class="na">length</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">builder</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">src</span><span class="o">,</span> <span class="n">offset</span><span class="o">,</span> <span class="n">src</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="n">offset</span><span class="o">);</span>
        <span class="o">}</span>
        <span class="k">return</span> <span class="n">builder</span><span class="o">.</span><span class="na">toString</span><span class="o">();</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></td></tr></table>
</div>
</div><p><code>GenericTokenParser</code>的三个属性显而易见，就是占位符的开始关键字符串<code>openToken</code>、结束关键字符串<code>closeToken</code>与占位符的处理器，这里的概念有点类似策略模式，定义了占位符处理器接口，解析器在一段字符串里找到占位符就交给处理器处理并替换占位符。而在这里只是处理了${}，在后续的解析中都会遇到该通用解析器</p>
<h2 id="总结">总结</h2>
<p>本篇文章讲述了<code>XMLConfigBuilder</code>如何自己封装的解析器去解析<code>mybatis-config.xml</code>，由基于Java提供对XML解析的API之上封装出来的<code>XPathParser</code>解析器与增强<code>XNode</code>节点，并且了解了如何解析配置文件里${}动态配置的替换，
部分相关的源码解析地址：<a href="https://github.com/Janyd/mybatis-3" target="_blank" rel="noopener noreffer">https://github.com/Janyd/mybatis-3</a>
</p>
<h2 id="参考链接">参考链接</h2>
<ul>
<li><a href="https://www.jianshu.com/p/c358a80155bc" target="_blank" rel="noopener noreffer">MyBatis（三） xml文件解析流程 动态SQL解析</a>
</li>
</ul>
</div><div class="post-footer" id="post-footer">
    <div class="post-info">
        <div class="post-info-line">
            <div class="post-info-mod">
                <span>本文于 2020-04-25 更新</span>
            </div>
            <div class="post-info-license"></div>
        </div>
        <div class="post-info-line">
            <div class="post-info-md"><span>
                            <a class="link-to-markdown" href="/mybatis%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90%E4%BA%8C-mybatis-config%E8%A7%A3%E6%9E%90/index.md" target="_blank">阅读原始文档</a>
                        </span></div>
            <div class="post-info-share">
                <span><a href="javascript:void(0);" title="分享到 微博" data-sharer="weibo" data-url="http://example.org/mybatis%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90%E4%BA%8C-mybatis-config%E8%A7%A3%E6%9E%90/" data-title="Mybatis加载流程分析（二）- mybatis-config解析"><i class="fab fa-weibo fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Evernote" data-sharer="evernote" data-url="http://example.org/mybatis%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90%E4%BA%8C-mybatis-config%E8%A7%A3%E6%9E%90/" data-title="Mybatis加载流程分析（二）- mybatis-config解析"><i class="fab fa-evernote fa-fw"></i></a></span>
            </div>
        </div>
    </div>

    <div class="post-info-more">
        <section><i class="fas fa-tags fa-fw"></i>&nbsp;<a href="/tags/mybatis">Mybatis</a>,&nbsp;<a href="/tags/%E6%BA%90%E7%A0%81">源码</a></section>
        <section>
            <span><a href="javascript:window.history.back();">返回</a></span>&nbsp;|&nbsp;<span><a href="/">主页</a></span>
        </section>
    </div>

    <div class="post-nav"><a href="/mybatis%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90%E4%B8%80-%E5%BC%80%E7%AF%87/" class="prev" rel="prev" title="Mybatis加载流程分析（一）- 开篇"><i class="fas fa-angle-left fa-fw"></i>Mybatis加载流程分析（一）- 开篇</a>
            <a href="/mybatis%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90%E4%B8%89-%E5%8A%A0%E8%BD%BDmybatis-config.xml%E5%86%85%E5%AE%B9/" class="next" rel="next" title="Mybatis加载流程分析(三) - 加载mybatis-config.xml内容">Mybatis加载流程分析(三) - 加载mybatis-config.xml内容<i class="fas fa-angle-right fa-fw"></i></a></div>
</div>
<div class="comment"></div>
    </article></div>
            </main><footer class="footer">
    <div class="copyright"><div class="copyright-line">由 <a href="https://gohugo.io/" target="_blank" rel="external nofollow noopener noreffer">Hugo</a> 强力驱动 | 主题 - <a href="https://github.com/dillonzq/LoveIt" target="_blank" rel="external nofollow noopener noreffer"><i class="far fa-heart fa-fw"></i> LoveIt</a>
        </div>

        <div class="copyright-line"><i class="far fa-copyright fa-fw"></i><span itemprop="copyrightYear">2020</span><span class="author" itemprop="copyrightHolder">&nbsp;<a href="https://github.com/Janyd" target="_blank">Janyd</a></span>&nbsp;|&nbsp;<span class="license"><a rel="license external nofollow noopener noreffer" href="https://creativecommons.org/licenses/by-nc/4.0/" target="_blank">CC BY-NC 4.0</a></span></div>
    </div>
</footer>
</div><a href="#" class="dynamic-to-top animated faster" id="dynamic-to-top">
            <i class="fas fa-chevron-up fa-fw"></i>
        </a><script>
        document.addEventListener('DOMContentLoaded', function () {
            lightGallery(document.getElementById('content'), {
                selector: '.lightgallery',
                speed: 400,
                hideBarsDelay: 2000,
                thumbnail: true,
                exThumbImage: 'data-thumbnail',
                thumbWidth: 80,
                thumbContHeight: 80,
            });
        });
    </script><script>
        window.mermaidArr = ["id-d23ec7"];
    </script><script>
        window.contentMap = {"id-d23ec7":"\r\nclassDiagram\r\n    class BaseBuilder\r\n    \u003c\u003cabstract\u003e\u003e BaseBuilder\r\n    class XMLConfigBuilder\r\n    class XMLMapperBuilder\r\n    class XMLStatementBuilder\r\n    class XMLSqlSourceBuilder\r\n    class XMLScriptBuilder\r\n    class MapperBuilderAssistant\r\n    BaseBuilder \u003c|-- XMLConfigBuilder\r\n    BaseBuilder \u003c|-- XMLMapperBuilder\r\n    BaseBuilder \u003c|-- XMLStatementBuilder\r\n    BaseBuilder \u003c|-- XMLSqlSourceBuilder\r\n    BaseBuilder \u003c|-- XMLScriptBuilder\r\n    BaseBuilder \u003c|-- MapperBuilderAssistant\r"};
    </script><link rel="stylesheet" href="/lib/lightgallery/lightgallery.min.css"><link rel="stylesheet" href="/lib/mermaid/mermaid.css"><script src="/lib/smooth-scroll/smooth-scroll.polyfills.min.js"></script><script src="/lib/sharer/sharer.min.js"></script><script src="/lib/lazysizes/lazysizes.min.js"></script><script src="/lib/lightgallery/lightgallery.min.js"></script><script src="/lib/lightgallery/lg-thumbnail.min.js"></script><script src="/lib/lightgallery/lg-zoom.min.js"></script><script src="/lib/mermaid/mermaid.min.js"></script><script src="/js/theme.min.js"></script></body>
</html>
